

#include "CAxialParticleSystemSceneNode.h"
#include "ISceneManager.h" 
#include "ICameraSceneNode.h"
#include "IVideoDriver.h"
#include <stdio.h>

namespace irr   
{
namespace scene 
{


//! constructor
CAxialParticleSystemSceneNode::CAxialParticleSystemSceneNode(
		ISceneNode* parent, ISceneManager* mgr, s32 id,
		core::dimension2d<s32>imagecount, s32 mirrory )
			
: ISceneNode(parent, mgr, id),
  MirrorY(mirrory),
  ImageCount(imagecount),
  lastEmitTime(0),
  Axis(1) // defaults to Y axis like mariokart
{
	#ifdef _DEBUG
	setDebugName("CAxialParticleSystemSceneNode");
	#endif

	setParticleSize();
}



//! destructor
CAxialParticleSystemSceneNode::~CAxialParticleSystemSceneNode()
{

}



//! Returns the material based on the zero based index i.
video::SMaterial& CAxialParticleSystemSceneNode::getMaterial(u32 i)
{
	return Material;
}



//! Returns amount of materials used by this scene node.
u32 CAxialParticleSystemSceneNode::getMaterialCount()
{
	return 1;
}




//! pre render event
void CAxialParticleSystemSceneNode::OnRegisterSceneNode()
{
	if (IsVisible)
	{

		if (Particles.size() != 0)
		{
			SceneManager->registerNodeForRendering(this);
			ISceneNode::OnRegisterSceneNode();
		}
	}
}
 


//! render
void CAxialParticleSystemSceneNode::render()
{
	video::IVideoDriver* driver = SceneManager->getVideoDriver();
	ICameraSceneNode* camera = SceneManager->getActiveCamera();

	if (!camera || !driver)
		return;



	// reallocate arrays, if they are too small
	reallocateBuffers();

	// create particle vertex data
	for (u32 i=0; i<Particles.size(); ++i)
	{
		CAxialParticle& particle = Particles[i];

		// calculate vectors for letting particles look to camera
		core::vector3df campos = camera->getAbsolutePosition();
		core::vector3df target = camera->getTarget(); // particle.pos; //;camera->getTarget();
		core::vector3df up =   camera->getUpVector(); // core::vector3df(0,1,0);//
		core::vector3df view = target - campos;
		core::matrix4 m;
 
		m.setRotationDegrees(particle.rot);
		m.rotateVect(up);

		view.normalize();
		core::vector3df h = up.crossProduct(view);
		h.normalize();
		core::vector3df v = h.crossProduct(view);
		v.normalize();
		view *= -1.0f;

		core::vector3df horizontal = h;
		horizontal *= 0.5f * particle.size;
		core::vector3df vertical = v;
		vertical   *= 0.5f * particle.size;

		s32 idx = i*4;

		Vertices[0+idx].Pos = particle.pos + horizontal + vertical;
		Vertices[0+idx].Color = particle.color;
		Vertices[0+idx].Normal = view;

		Vertices[1+idx].Pos = particle.pos + horizontal - vertical;
		Vertices[1+idx].Color = particle.color;
		Vertices[1+idx].Normal = view;

		Vertices[2+idx].Pos = particle.pos - horizontal - vertical;
		Vertices[2+idx].Color = particle.color;
		Vertices[2+idx].Normal = view;

		Vertices[3+idx].Pos = particle.pos - horizontal + vertical;
		Vertices[3+idx].Color = particle.color;
		Vertices[3+idx].Normal = view;
		
		// get rotation sprite number
		core::vector3df targetrot;
		targetrot=getTargetAngle(particle.pos,campos);
		
		m.rotateVect(view);
		
		targetrot=getTargetAngle(-view,core::vector3df(0,0,0));
		targetrot-=particle.rot;
		
		f32 rotadd;
		// axis is Y 
		if (Axis == 0)
		switch(Axis)
		{
			case 0: // x
				 rotadd = targetrot.X-particle.rot.X;
				 break;
			case 1: // y
				 rotadd = targetrot.Y-particle.rot.Y;
				 break;
			case 2: // z
				 rotadd = targetrot.Z-particle.rot.Z;
				 break;
		}
		
		rotadd=fmod(rotadd,360.0f);
	   
		s16 imagex = int(rotadd / (360.0/ImageCount.Width));
			   
		// get the texture position
		f32 x1	 = (1.0 / ImageCount.Width)  * imagex;
		f32 y1	 = (1.0 / ImageCount.Height) * particle.sprite;
		f32 x2	 = (1.0 / ImageCount.Width)  * (imagex+1);
		f32 y2	 = (1.0 / ImageCount.Height) * (particle.sprite+1);
		   
		// set texture coords
		Vertices[0+idx].TCoords.set(x2, y2);
		Vertices[1+idx].TCoords.set(x2, y1);
		Vertices[2+idx].TCoords.set(x1, y1);
		Vertices[3+idx].TCoords.set(x1, y2);
	}

	driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
	driver->setMaterial(Material);

	driver->drawIndexedTriangleList(Vertices.pointer(), Particles.size()*4,
		Indices.pointer(), Particles.size()*2);

	// for debug purposes only:
	if (DebugDataVisible)
	{
		driver->setTransform(video::ETS_WORLD, AbsoluteTransformation);
		video::SMaterial m;
		m.Lighting = false;
		driver->setMaterial(m);
		driver->draw3DBox(Box, video::SColor(0,255,255,255));
	}
}



//! returns the axis aligned bounding box of this node
const core::aabbox3d<f32>& CAxialParticleSystemSceneNode::getBoundingBox() const
{
	return Box;
}

void CAxialParticleSystemSceneNode::OnAnimate(u32 time)
{
	 doParticleSystem(time);
	 this->ISceneNode::OnAnimate(time);
}


void CAxialParticleSystemSceneNode::doParticleSystem(u32 time)
{
	if (lastEmitTime==0) lastEmitTime = time;
	u32 now = time;
	u32 timediff = now - lastEmitTime;
	lastEmitTime = now;

	Box.reset(core::vector3df(0,0,0));

	// animate all particles
	f32 scale = (f32)timediff;

	for (s32 i=0; i<(s32)Particles.size();)
	{
		
		if (now > Particles[i].endTime && Particles[i].endTime != 0)
			Particles.erase(i);
		else
		{
			Particles[i].pos += (Particles[i].velocity * scale);
			Particles[i].rot += (Particles[i].spin * scale);
			Box.addInternalPoint(Particles[i].pos);
			++i;
		}
	} 

	f32 m = ParticleSize.Width > ParticleSize.Height ? ParticleSize.Width : ParticleSize.Height;
	m *= 0.5f;
	Box.MaxEdge.X += m;
	Box.MaxEdge.Y += m;
	Box.MaxEdge.Z += m;

	Box.MinEdge.X -= m;
	Box.MinEdge.Y -= m;
	Box.MinEdge.Z -= m;

}

void CAxialParticleSystemSceneNode::addParticles(s32 count)
{
	 Particles.set_used(Particles.size() + count);
	 reallocateBuffers();
}

//! Sets the size of all particles.
void CAxialParticleSystemSceneNode::setParticleSize(const core::dimension2d<f32> &size)
{
	ParticleSize = size;
}


void CAxialParticleSystemSceneNode::reallocateBuffers()
{
	if (Particles.size() * 4 > Vertices.size() ||
		Particles.size() * 6 > Indices.size())
	{
		s32 oldSize = Vertices.size();
		Vertices.set_used(Particles.size() * 4);

		u32 i;

		// fill remaining vertices
		for (i=oldSize; i<Vertices.size(); i+=4)
		{
			Vertices[0+i].TCoords.set(0.0f, 0.0f);
			Vertices[1+i].TCoords.set(0.0f, 1.0f);
			Vertices[2+i].TCoords.set(1.0f, 1.0f);
			Vertices[3+i].TCoords.set(1.0f, 0.0f);
		}

		// fill remaining indices
		s32 oldIdxSize = Indices.size();
		s32 oldvertices = oldSize;
		Indices.set_used(Particles.size() * 6);
		
		for (i=oldIdxSize; i<Indices.size(); i+=6)
		{
			Indices[0+i] = 0+oldvertices;
			Indices[1+i] = 2+oldvertices;
			Indices[2+i] = 1+oldvertices;
			Indices[3+i] = 0+oldvertices;
			Indices[4+i] = 3+oldvertices;
			Indices[5+i] = 2+oldvertices;
			oldvertices += 4;
		}
	}
}


} // end namespace scene
} // end namespace irr

